/*
 * Copyright (c) 2007, TopCoder, Inc. All rights reserved.
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using Studentium;
using TopCoder.Util.Calendar.Entry;
using TopCoder.WinForms.Controls.CalendarView.Views;

namespace TopCoder.WinForms.Controls.CalendarView
{
    /// <summary>
    /// <para>This class is the main control for the component.</para>
    /// <para>This class is a basic holder for two different items: the actual calendar view and the navigation
    /// buttons used to switch between different view types, like daily or quarterly. This class holds a mapping
    /// of calendar view type description strings to <see cref="AbstractCalendarView"/> instances, used when
    /// switching between views. This control holds a panel that is the location of the view. When the view type
    /// is changed, the panel's control it contains is switched from the current view control to the new view
    /// control. The values in this enum can be changed from the defaults, allowing for further view types to be
    /// used, instead of just the defaults. This class also has the additional ability to display the navigation
    /// control at either the top or the bottom of the control, depending on user preferences.</para>
    /// </summary>
    /// <remarks>Thread Safety: This class is mutable, as its state can change after instantiation.</remarks>
    /// <author>Ghostar</author>
    /// <author>yext</author>
    /// <author>argolite</author>
    /// <author>alex.dolin</author>
    /// <version>1.1</version>
    /// <copyright>Copyright (c) 2007, TopCoder, Inc. All rights reserved.</copyright>
    public class CalendarViewControl : UserControl
    {
        /// <summary>
        /// Name of the day view.
        /// </summary>
        private const string DAY = "Day";

        /// <summary>
        /// Name of the week view.
        /// </summary>
        private const string WEEK = "Week";

        /// <summary>
        /// Name of the month view.
        /// </summary>
        private const string MONTH = "Month";

        /// <summary>
        /// Name of the quarter view.
        /// </summary>
        private const string QUARTER = "Quarter";

        /// <summary>
        /// Name of the year view.
        /// </summary>
        private const string YEAR = "Year";

        /// <summary>
        /// This member variable is the panel where the calendar view is displayed. This panel is initialized to hold
        /// the DailyCalendarView instance, but that will change as the user uses the navigation controls to change the
        /// display type selected, like daily or weekly. This member variable is initialized in the constructor and
        /// doesn't change after instantiation. It can't be null.
        /// </summary>
        private readonly Panel viewPanel;

        /// <summary>
        /// This IDictionary holds a mapping of string description values to AbstractCalendarView instances. This
        /// IDictionary is used when switching the view of the calendar, for example from a daily view to a monthly
        /// view. The "CurrentViewType" property setter gets the AbstractCalendarView associated with the given value
        /// from this collection and changes the control in the viewPanel member variable to show the new calendar
        /// view. The values in this IDictionary can't be null, and its contents can be modified after instantiation
        /// through the add / get / remove view methods, allowing for custom implementations to be added for specific
        /// calendar view types. The keys can't be empty strings or null. It can't be null.
        /// </summary>
        private readonly IDictionary<string, AbstractCalendarView> views =
            new Dictionary<string, AbstractCalendarView>();

        /// <summary>
        /// This member variable holds the current view type description string of the calendar view that is being
        /// displayed. This value is initially "Day", as the daily calendar view is the default. This value is set
        /// through the CurrentViewType property, can't be null or an empty string.
        /// </summary>
        private string currentViewType = "";

        /// <summary>
        /// This member variable holds the CalendarViewNavigationControl that contains the buttons for navigating the
        /// different calendar view types, like monthly and weekly. This value is initialized in the constructor to a
        /// new CalendarViewNavigationControl instance. This control is displayed intially at the top of the
        /// CalendarViewControl, but its position is modifiable through the NavigationControlPosition to either be at
        /// the top or the bottom of the control. It can't be null
        /// </summary>
        private readonly CalendarViewNavigationControl navigationControl;

        /// <summary>
        /// This member variable holds a mapping between specific types of calendar entries and specific colors,
        /// allowing the user to color entries in the display as they see fit. This collection holds a sets of colors
        /// that is common to all views contained in the control. The user can supplement this collection in the
        /// individual views, but this collection is representative of the base colors set in each  view.This
        /// IDictionary is initially empty, but it is modifiable through the SetColor method. It can't be null
        /// </summary>
        private readonly IDictionary<CalendarEntryType, Color> colors = new Dictionary<CalendarEntryType, Color>();

        /// <summary>
        /// This member variable holds the data currently available for display in all the calendar views. This
        /// member variable is used by the views when added to this control. This value is mutable, as it can be
        /// changed through the Data property. It cannot contain null values and is initialized to an empty
        /// List. It can't be null.
        /// </summary>
        private IList<CalendarEntry> data = new List<CalendarEntry>();


        private MainForm mainController;        

        public MainForm MainController
        {
            get { return mainController; }
            set { mainController = value; }
        } 

        /// <summary>
        /// <para>This property returns and sets the current view displayed in the control to the
        /// <see cref="AbstractCalendarView"/> that is registered with the given name. This property is used by
        /// the <see cref="CalendarViewNavigationControl"/> to switch the view depending on what view button was
        /// pressed.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If the given value is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If the given value is an empty string.</exception>
        /// <exception cref="CalendarViewControlException">
        /// If the given value doesn't match a registered <see cref="AbstractCalendarView"/> implementation.
        /// </exception>
        /// <value>The current view displayed in the control.</value>
        [Browsable(true), Category("Appearance"), Description("Initial calendar view shown.")]
        public string CurrentViewType
        {
            get
            {
                return currentViewType;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException(
                        "CurrentViewType", "CurrentViewType should not be set to null.");
                }
                if (value.Trim().Length == 0)
                {
                    throw new ArgumentException(
                        "CurrentViewType should not be set to an empty string.", "CurrentViewType");
                }

                if (!currentViewType.Equals(value))
                {
                    if (!views.ContainsKey(value))
                    {
                        throw new CalendarViewControlException(
                            "The given value doesn't match a registered AbstractCalendarView implementation.");
                    }

                    viewPanel.Controls.Clear();

                    AbstractCalendarView newView = views[value];

                    newView.Dock = DockStyle.Fill;
                    viewPanel.Controls.Add(newView);
                    currentViewType = value;
                }
            }
        }

        /// <summary>
        /// <para>Gets the <see cref="CalendarViewNavigationControl"/> that contains the buttons for navigating
        /// the different calendar view types, like monthly and weekly.</para>
        /// </summary>
        /// <value>The <see cref="CalendarViewNavigationControl"/> that contains the buttons.</value>
        public CalendarViewNavigationControl NavigationControl
        {
            get
            {
                return navigationControl;
            }
        }

        /// <summary>
        /// <para>This property sets the position of the <see cref="NavigationControl"/>, either to the top or
        /// the bottom of the <see cref="CalendarViewControl"/>. This way the user can choose to have the
        /// buttons controlling which view to show at either the top or the bottom of the control.</para>
        /// </summary>
        /// <exception cref="ArgumentException">If the value given isn't a valid enum value.</exception>
        [Browsable(true), Category("Appearance"), Description("Navigation button panel position.")]
        public NavigationControlPosition NavigationControlPosition
        {
            get
            {
                if (navigationControl.Dock == DockStyle.Top)
                {
                    return NavigationControlPosition.Top;
                }

                return NavigationControlPosition.Bottom;
            }
            set
            {
                if (value == NavigationControlPosition.Top)
                {
                    navigationControl.Dock = DockStyle.Top;
                }
                else if (value == NavigationControlPosition.Bottom)
                {
                    navigationControl.Dock = DockStyle.Bottom;
                }
                else
                {
                    throw new ArgumentException(
                        "The value given isn't a valid enum value.", "NavigationControlPosition");
                }
            }
        }

        /// <summary>
        /// <para>This property allows the user to get the data of the control, and bind a certain set of data
        /// to the control.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If the given value is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If the <c>IList</c> contains a <c>null</c> value.
        /// </exception>
        public IList<CalendarEntry> Data
        {
            get
            {
                // Returns a shallow copy.
                return new List<CalendarEntry>(data);
            }

            set
            {
                if (value==null)
                {
                    //throw new ArgumentNullException("value", "Data should not be set to null.");
                    data = new List<CalendarEntry>();
                    return;
                }
                if (value.Contains(null))
                {
                    throw new ArgumentException("Data should not contain null elements.", "value");
                }

                // Sets a shallow copy.
                data = new List<CalendarEntry>(value);

                foreach (AbstractCalendarView view in views.Values)
                {
                    view.AllData = value;
                }
            }
        }

        /// <summary>
        /// <para>This constructor initializes the controls in the calendar view control, as well as the default
        /// views.</para>
        /// </summary>
        public CalendarViewControl()
        {
            // viewPanel
            viewPanel = new Panel();
            viewPanel.Name = "viewPanel";
            viewPanel.Dock = DockStyle.Fill;
            viewPanel.Size = new Size(400, Size.Height - 60);
            Controls.Add(viewPanel);

            // navigationControl
            navigationControl = new CalendarViewNavigationControl(this);
            navigationControl.Name = "navigationControl";
            navigationControl.Dock = DockStyle.Top;
            navigationControl.Size = new Size(400, 60);            
            Controls.Add(navigationControl);

            // views
            AddView(DAY, new DailyCalendarView(this));

            WeeklyCalendarView weeklyCalendarView = new WeeklyCalendarView(this);
            weeklyCalendarView.DayViewType = DAY;
            AddView(WEEK, weeklyCalendarView);

            MonthlyCalendarView monthlyCalendarView = new MonthlyCalendarView(this);
            monthlyCalendarView.DayViewType = DAY;
            monthlyCalendarView.WeekViewType = WEEK;
            AddView(MONTH, monthlyCalendarView);

            QuarterlyCalendarView quarterlyCalendarView = new QuarterlyCalendarView(this);
            quarterlyCalendarView.DayViewType = DAY;
            quarterlyCalendarView.WeekViewType = WEEK;
            quarterlyCalendarView.MonthViewType = MONTH;
            AddView(QUARTER, quarterlyCalendarView);

            YearlyCalendarView yearlyCalendarView = new YearlyCalendarView(this);
            yearlyCalendarView.DayViewType = DAY;
            yearlyCalendarView.WeekViewType = WEEK;
            yearlyCalendarView.MonthViewType = MONTH;
            AddView(YEAR, yearlyCalendarView);

            CurrentViewType = DAY;
        }

        /// <summary>
        /// <para>This method adds a new calendar view to the views collection under the given type description.
        /// </para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If either parameter is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If the <paramref name="type"/> is an empty string.</exception>
        /// <param name="type">The type description of the view to add.</param>
        /// <param name="view">The view to add.</param>
        public void AddView(string type, AbstractCalendarView view)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type", "Argument type should not be null.");
            }
            if (type.Trim().Length == 0)
            {
                throw new ArgumentException("Argument type should not be empty.", "type");
            }

            if (view == null)
            {
                throw new ArgumentNullException("view", "Argument view should not be null.");
            }
            if(view.Parent != this)
            {
                throw new ArgumentException("Argument view has incorrect Parent property.", "view");
            }

            foreach (KeyValuePair<CalendarEntryType, Color> pair in colors)
            {
                view.SetColor(pair.Key, pair.Value);
            }

            view.AllData = Data;

            views[type] = view;
        }

        /// <summary>
        /// <para>This method returns the <see cref="AbstractCalendarView"/> associated with the given type
        /// description string given. If no value is found that matches the given type string, a <c>null</c>
        /// value is returned.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If <paramref name="type"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="type"/> is an empty string.</exception>
        /// <param name="type">The type description string of the calendar view to return.</param>
        /// <returns>The calendar view that matches the type description string, or <c>null</c> if no match
        /// was found.</returns>
        public AbstractCalendarView GetView(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type", "Argument type should not be null.");
            }
            if (type.Trim().Length == 0)
            {
                throw new ArgumentException("Argument type should not be empty.", "type");
            }

            if (views.ContainsKey(type))
            {
                return views[type];
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// <para>This method removes the <see cref="AbstractCalendarView"/> from the views that matches the
        /// type parameter given. If no match is found for the given string, no work is done.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If <paramref name="type"/> is <c>null</c>.</exception>
        /// <exception cref="ArgumentException">If <paramref name="type"/> is an empty string.</exception>
        /// <param name="type">The type description string of the calendar view to remove.</param>
        public void RemoveView(string type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type", "Argument type should not be null.");
            }
            if (type.Trim().Length == 0)
            {
                throw new ArgumentException("Argument type should not be empty.", "type");
            }

            views.Remove(type);
        }

        /// <summary>
        /// <para>This method returns the color for the given entry type. If no color is found for that specific
        /// entry type, a value of <see cref="Color.Transparent"/> is returned.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If <paramref name="entryType"/> is <c>null</c>.
        /// </exception>
        /// <param name="entryType">The entry type to get the color for.</param>
        /// <returns>The color for the given entry type.</returns>
        public Color GetColor(CalendarEntryType entryType)
        {
            if (entryType == null)
            {
                throw new ArgumentNullException("entryType", "Argument entryType should not be null.");
            }

            if (colors.ContainsKey(entryType))
            {
                return colors[entryType];
            }

            return Color.Transparent;
        }

        /// <summary>
        /// <para>This method sets the color for the entry type.</para>
        /// </summary>
        /// <exception cref="ArgumentNullException">If <paramref name="entryType"/> is <c>null</c>.
        /// </exception>
        /// <param name="entryType">The entry type to set the color for.</param>
        /// <param name="color">The color to use.</param>
        public void SetColor(CalendarEntryType entryType, Color color)
        {
            if (entryType == null)
            {
                throw new ArgumentNullException("entryType", "Argument entryType should not be null.");
            }

            colors[entryType] = color;
            foreach (AbstractCalendarView view in views.Values)
            {
                view.SetColor(entryType, color);
            }
        }

    }
}
